<template>
    <div class="flex flex-col h-full overflow-hidden">
        <el-scrollbar ref="scrollbarRef" height="100%">
            <div class="m-5 p-4 rounded-xl bg-page">
                <div class="flex items-center text-tx-secondary">
                    <icon name="el-icon-Tickets" />
                    <span class="ml-2">文档提取完成</span>
                </div>

                <div v-if="fileStatus === 1" class="flex items-center mt-4 text-primary">
                    <icon name="el-icon-Refresh" class="is-loading" />
                    <span class="ml-2">正在分析文档</span>
                </div>

                <div v-else class="flex items-center mt-4 text-tx-secondary">
                    <template v-if="fileStatus === 0">
                        <icon name="el-icon-Refresh" class="is-loading" />
                        <span class="ml-2">正在排队分析</span>
                    </template>
                    <template v-if="fileStatus === 1">
                        <icon name="el-icon-Refresh" class="is-loading" />
                        <span class="ml-2">文档正在分析</span>
                    </template>
                    <template v-if="fileStatus === 2">
                        <icon name="el-icon-Refresh" />
                        <span class="ml-2">文档分析完毕</span>
                    </template>
                    <template v-if="fileStatus === 3">
                        <icon name="el-icon-Refresh" />
                        <span class="ml-2">文档分析失败</span>
                    </template>
                </div>
                <div v-if="fileStatus === 2" class="flex items-center mt-4 text-tx-secondary">
                    <icon name="el-icon-ChatLineRound" />
                    <span class="ml-2">现在您可以对文档进行提问啦~</span>
                </div>
                <div v-if="fileStatus === 3" class="flex items-center mt-4 text-error">
                    <icon name="el-icon-Warning" />
                    <span class="ml-2">失败原因：{{ fileError || '-' }}</span>
                </div>
            </div>

            <div ref="innerRef" class="mx-5 h-full overflow-hidden">
                <chatting-message
                    v-for="(item, index) in historyList"
                    :key="index"
                    :type="item.type"
                    :recordId="item.id"
                    :avatar="item.avatar"
                    :thinks="item.thinks"
                    :content="item.content"
                    :quotes="item.quotes"
                    :createTime="item.create_time"
                    :loading="item.loading"
                    :isShowStop="false"
                    :isShowCopy="true"
                    :isShowQuote="true"
                    :isShowRewrite="true"
                    :isContextClear="!!item.end_clear"
                    @rewrite="rewriteChat"
                />
            </div>
        </el-scrollbar>

        <div class="relative m-5">
            <el-button
                v-if="isReceiving"
                type="primary"
                class="absolute -top-10 left-[250px]"
                @click="streamReader?.cancel()"
            >
                <icon name="el-icon-Refresh" class="is-loading" />
                <span>停止输出</span>
            </el-button>

            <chatting-input-box
                ref="chatActionRef"
                :loading="isReceiving"
                :disabled="fileStatus !== 2"
                :isShowClear="true"
                @sending="sendChats"
                @clear="handleClear"
            />
        </div>
    </div>
</template>

<script setup lang="ts">
import { ElScrollbar } from 'element-plus'
import { useElementSize, watchThrottled } from '@vueuse/core'
import useUserStore from '~/stores/user'
import datetimeUtil from '~/utils/datetime'
import docsFilesApi from '~/api/apply/docs/files'
import docsRecordApi from '~/api/apply/docs/record'

const props = defineProps({
    // 文件编号
    fileCode: {
        type: String,
        default: ''
    },
    // 文件状态: [0=待训练,1=训练中,2=训练成功,3=训练失败]
    fileStatus: {
        type: Number,
        default: 0
    },
    // 失败原因
    fileError: {
        type: String,
        default: ''
    },
    // 对话头像
    chatAvatar: {
        type: String,
        default: ''
    }
})

const userStore = useUserStore()

// 节点Ref
const chatActionRef = shallowRef()
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
const innerRef = ref<HTMLDivElement>()

// 对话参数
const isReceiving = ref<boolean>(false)
let streamReader: ReadableStreamDefaultReader<Uint8Array> | null = null

// 记录列表
const historyList = ref<DocsRecordListResponse[]>([])

/**
 * 获取对话记录
 */
const queryHistoryList = async () => {
    historyList.value = await docsRecordApi.lists({
        scene: 'dialogue',
        code: props.fileCode
    })
    await nextTick(() => {
        scrollToBottom()
    })
}

/**
 * 清空对话上下文
 */
const handleClear = async () => {
    if (isReceiving.value) {
        feedback.msgError('请耐心等待回复完毕再操作!')
    } else {
        feedback.confirm('确定要清空当前对话记录吗?')
            .then(async () => {
                await docsRecordApi.clear({
                    scene: 'dialogue',
                    code: props.fileCode
                })
                const chatNum = historyList.value.length
                if (chatNum > 0) {
                    historyList.value[chatNum - 1].end_clear = true
                }
                feedback.msgSuccess('历史对话已清空!')
                await nextTick(() => {
                    scrollToBottom()
                })
            }).catch(() => {})
    }
}

/**
 * 重新回答问题
 */
const { lockFn: rewriteChat } = useLockFn(async () => {
    const last = historyList.value[historyList.value.length - 1]
    const userInput = historyList.value.find(({ id }) => id === last.id)
    if (userInput) {
        historyList.value.splice(historyList.value.length - 2, 2)
        await sendChats(
            userInput.content
        )
    }
})

/**
 * 发起对话
 */
const sendChats = async (question: string) => {
    if (props.fileStatus === 0) {
        return feedback.msgWarning('请耐心等待文档分析完毕再提问!')
    }

    const datetime = datetimeUtil.datetime()
    historyList.value.push({
        id: 0,
        type: 'question',
        avatar: useUserStore().user.avatar,
        content: question,
        create_time: datetime
    })

    historyList.value.push({
        id: 0,
        type: 'answer',
        avatar: props.chatAvatar,
        thinks: '',
        content: '',
        create_time: datetime,
        loading: true
    })

    chatActionRef.value?.setInputValue()
    isReceiving.value = true
    try {
        let chatIndex = -1
        await docsFilesApi.sendChat({
            type: 'dialogue',
            code: props.fileCode,
            question: question
        }, {
            onstart(reader) {
                streamReader = reader
            },
            onmessage(value) {
                value.trim().split('data:').forEach(async (text) => {
                    if (text !== '' && text !== '[DONE]') {
                        try {
                            const dataJson = JSON.parse(text)
                            if (dataJson.error) {
                                feedback.msgError(dataJson.error.message)
                            }

                            const { id, choices } = dataJson

                            const event = choices[0]?.finish_reason
                            const answer  = choices[0]?.delta.content
                            const thinks  = choices[0]?.delta.reasoning_content

                            chatIndex = historyList.value.findIndex((item: any) => item.id === id)
                            if (chatIndex === -1) {
                                chatIndex = historyList.value.length - 1
                                if (id) {
                                    historyList.value[chatIndex].id = id
                                }
                            }

                            if (thinks) {
                                historyList.value[chatIndex].thinks += thinks
                            }

                            if (answer) {
                                historyList.value[chatIndex].content += answer
                            }

                            if (event === 'normal') {
                                historyList.value[chatIndex].loading = false
                                scrollToBottom()
                            }
                        } catch {}
                    }
                })
            },
            async onclose() {
                scrollToBottom()
                setTimeout(queryHistoryList, 1200)
                isReceiving.value = false
                await userStore.getUser()
            }
        })
    } catch {
        isReceiving.value = false
        scrollToBottom()
        streamReader?.cancel()
    }

    await nextTick(async () => {
        scrollToBottom()
    })
}

/**
 * 滚动条到底部
 */
const scrollToBottom = () => {
    const scrollHeight: number | any = scrollbarRef.value?.wrapRef?.scrollHeight
    const clientHeight: number | any = scrollbarRef.value?.wrapRef?.clientHeight
    const scrollH = scrollHeight - clientHeight
    scrollbarRef.value?.setScrollTop(scrollH)
}

/**
 * 监听高度变化
 */
const { height } = useElementSize(innerRef)
watchThrottled(
    height,
    () => {
        if (isReceiving.value) {
            scrollToBottom()
        }
    },
    { throttle: 500, immediate: true }
)

/**
 * 监听文件变化
 */
watchThrottled(
    () => props.fileCode,
    async (val) => {
        if (val) {
            await queryHistoryList()
            scrollToBottom()
        }
    }, { throttle: 500, immediate: true }
)
</script>
